#ifndef __VEINWALKER__H
#define __VEINWALKER__H
#include "types.h"
#include "ViewWithThreshold.h"
#include "Point.h"
#include "Averagizer.h"
#include "Math.h"
#include "Point.h"
#include "RunFinder.h"
#include <vector>

class VeinWalker : protected ViewBW {
public:
	/** variables that affect our algorithm for "walking" */
	struct Criteria {
		Criteria() : m_bDetectByMin(false), m_bNoHillClimbing(false), 
		             m_bFailDueToAngleOutOfRange(false),
		             m_nMinHorizonDist(0),
		             m_nMaxAngleDist(30),
		             m_nMaxPeepDist(15), 
		             m_nMaxDist(INFINITY), 
		             m_nMaxIterations(-1),
		             m_nLoopPreventEvery(1),
		             m_nLoopPreventionDistance(0),
		             m_nSegmentForAngleCheck(0)
		{} 
	
		/** affects the angle detection done by Detect() */
		bool m_bDetectByMin;
		/** do we wish to use hill climbing algorithm in WalkUntil() */
		bool m_bNoHillClimbing;
		/** do we allow to fail due to angle being out of range
		 *  or do we always continue to walk in angles in our range
		 *  even if we need to do it artifically
		 *  example of improvment: veins_011_P1010015.jpg.pgm, P1010006
		 */
		bool m_bFailDueToAngleOutOfRange;
		/** let X be the number of pixels that a ray goes forward 
		 *  (where forward is determined by current angle)
		 *  before it stops, in all directions which are in the angle range.
		 *  if X is less than this number, than we enter "horizonless" state
		 *  otherwise, we leave "horizonless" state.
		 *  
		 *  if our view state vector ends with "horizonless" view states, we 
		 *  remove them.
		 *
		 *  being in "horizonless" state for too long will cause to stop 
		 *  (see m_nMaxAllowedHorizonlessWalks)
		 *  if this member is 0, it is ignored.
		 *
		 */
		U32 m_nMinHorizonDist;
		/** if we are in "horizonless" state for more than m_nMaxAllowedHorizonlessWalks
		 *  WalkSingle()s, we stop walking and return the path WITHOUT the states
		 *  we got from walking in the "horizonless" area.
		 */
		U32 m_nMaxAllowedHorizonlessWalks;
		/** this one is ignored if m_bFailDueToAngleOutOfRange is false */
		F64 m_nMaxAngleDist;
		/** how much we peep forward for hill climbing */
		F64 m_nMaxPeepDist;
		/** INFINITY means, ignore this parameter */
		F64 m_nMaxDist;
		/** maximal number of walking iterations, negative number means no bound */
		S32 m_nMaxIterations; 
		/** we want to prevent infinite loops
		 *  so every m_nLoopPreventEvery we check that we moved at least a distance
		 *  of m_nLoopPreventionDistance
		 */
		U32 m_nLoopPreventEvery;
		/** we want to prevent infinite loops
		 *  so every m_nLoopPreventEvery we check that we moved at least a distance
		 *  of m_nLoopPreventionDistance,
		 *  this is ignored if it is set to 0 or less than 0
		 */
		F64 m_nLoopPreventionDistance;
		/** we sometimes divert a bit from our "straight" direction in order
		 *  to go around obstacles, in order to select a direction in a junction
		 *  or for some other reason, these divetrions can cause us 
		 *  to move in angle which is outside our range but won't be detected
		 *  as such by normal angle checks (as the "diversion" is not calculated 
		 *  in our normal angle checks)
		 *  therefore, we compare the angle between the current point
		 *  and the point in the Viewstate that's "m_nSegmentForAngleCheck"
		 *  behind us and see if it is not out of range.
		 *  if this number is 0, it is ignored.
		 *  example of improvment: 153.pgm (without this we may divert too much)
		 *  @TODO should be calculated based on distance, instead of loop cycles
		 */
		U32 m_nSegmentForAngleCheck;
	};

	struct Info {
		std::vector < ViewState > m_vecStates1;
		Point<F64> m_p1;
		std::vector < ViewState > m_vecStates2;
		Point<F64> m_p2;
		F64 m_nAverageThickness; 
		F64 Dist2() { return m_p1.Dist2From(&m_p2); }
	};
	
	VeinWalker(const Image *pImage, F64 nRow, F64 nCol, F64 nAngle, 
	           const Criteria *pCriteria);
	F64 GetAngle() const { return GetCenterAngle(); }
	F64 GetRow() const   { return GetCenterRow(); }
	F64 GetCol() const   { return GetCenterCol(); }
	S32 GetThickness() { return m_nThickness; }
	const View *GetView() const { return View::GetThis(); }
	class StopReason {
		friend class VeinWalker;
	public:
		S32 CanWalk() { return m_nMask & 1; }
		S32 Far() { return m_nMask & 2; }
		S32 Turn() { return m_nMask & 4; }
		S32 IteratedEnough() { return m_nMask & 8; }
		S32 LostHorizon() { return m_nMask & 16; }
		S32 Looped() { return m_nMask & 32; }
		S32 Diverted() { return m_nMask & 64; }
	
		S32 OnlyFar() { return m_nMask == 2; }
	private:
		StopReason() : m_nMask(0) {}
		void SetCanWalk() { m_nMask |= 1; }
		void SetFar() { m_nMask |= 2; }
		void SetTurn() { m_nMask |= 4; }
		void SetIteratedEnough() { m_nMask |= 8; }
		void SetLostHorizon() { m_nMask |= 16; }
		void SetLooped() { m_nMask |= 32; }
		void SetDiverted() { m_nMask |= 64; }
		S32 m_nMask;
	};
	/** walks on a vein (uni directional) as much as it can according to the criteria */
	StopReason WalkUntil(Averagizer<> *pAvg, std::vector<ViewState> *pVec);
	/** calls WalkUntil to both sides of a point in a vein and collects info */
	void GetVeinInfo(bool bUniDirectional, Info *pVi);
	operator bool() { return GetPix(0, 0); }
	
	/** this routine is needed for us to be able to work correctly with RunFinder
	 *  (compile-time polymorphism)
	 */
	U8 GetPix(F64 nRow, F64 nCol) const { return ViewBW::GetPix(nRow, nCol); } 
private:
	struct WalkUntilContext {
		WalkUntilContext(std::vector<ViewState> *pVec, F64 nAngle) : 
			m_nIterations(0), 
			m_nHorizonlessIteraions(0),
			m_pVec(pVec),
			m_nFirstOrigAngle(nAngle) {}
		S32 m_nIterations;
		U32 m_nHorizonlessIteraions;
		std::vector<ViewState> *m_pVec;
		F64 m_nFirstOrigAngle; 
	};
	/** helper function used by WalkSingle when it "peeps"
	 *  should be called from the peeping VeinWalker
	 *  to initialize it's ViewState according to the first run of the
	 *  selected peeping path
	 *  @param returns which row was selected (the col is 1.0) through the eye of the parent
	 */
	VeinWalker(const VeinWalker *pWalkerParent, Run r, S32 *pnSelectedRow, const Criteria *pCrit);
	/** @param nAngleForHorizon - if we already have an angle
	 *                            this tells Detect() to check horizon in 
	 *                            this angle and in angles with distance less than 
	 *                            m_crit.m_nMaxAngleDist from this angle
	 *                            if this m_crit.m_bFailDueToAngleOutOfRange is on
	 *                            this angle will also be used as a range limiter
	 *                            so that we only detect in a range less than m_crit.m_nMaxAngleDist
	 *                            and nAngleForHorizon.
	 */
	void Detect(F64 nAngleForHorizon);	
	/** helper function used by WalkSingle when it "peeps"
	 *  this function determines, from all possible paths, which one is the best
	 */
	U32 GetPathScore(StopReason sr, U32 nAngleDist, S32 nRow);
	/** @param nOrigRow - original row from which WalkUntil started calling us
	 *  @param nOrigCol - original col from which WalkUntil started calling us
	 *  @param pContext - context info of the WalkUntil that called us
	 *  @param nAngleToDetectAround - angle given to inner calls to Detect() 
	 */
	bool WalkSingle(F64 nOrigRow, F64 nOrigCol, const WalkUntilContext *pCtx);
	/** Called by WalkSingle when it attempts to do peeps
	 *  (which "continue" the WalkUntil that called WalkSingle)
	 *  @param pContextPrev - context info about the "WalkUntil" which we are supposedly continuing
	 */
	StopReason WalkUntilContinue(Averagizer<> *pAvg, std::vector<ViewState> *pVec_,
                                     const WalkUntilContext *pContextPrev);
	/** helper routine for WalkUntil */
	bool CountHorizonlessWalks(U32 *pnHorizonlessIterations,
	                           const Averagizer<> *pAvg,
	                           Averagizer<> *pAvgThicknessBeforeHorizonless);
	/** helper routine for WalkUntil */
	bool PreventLoop(std::vector<ViewState> *pVec);
	/** helper routine for WalkUntil */
	bool PreventDiversion(F64 nAngle, const std::vector<ViewState> *pVec, 
	                      const std::vector<ViewState> *pPrevVec);
private:
	
	// results given out by Detect()
	/** straight walk minimum */
	U32 m_nThickness; 
	/** straight walk maximum distance */
	U32 m_nStraightWalkMax;
	/** size of our "horizon", see m_nMinHorizonDist */
	U32 m_nHorizon;
	/** the Criteria for the walking algorithm */
	Criteria m_crit;
};
#endif

